package cat.inkubator.plugin4j.updater;

import cat.inkubator.plugin4j.xml.Plugin;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

/**
 *
 * @author gato
 */
public class UpdateManifest {

    private Plugin plugin;

    public UpdateManifest(Plugin plugin) {
        this.plugin = plugin;
    }

    public void updateManifestFromPlugin(ArrayList<Plugin> toUpdate) {

        this.copyJar( true );
        Manifest manifest = this.unJar();
        manifest = this.updateManifestContent(manifest, toUpdate);
        this.createJar(manifest);
        this.copyJar( false );

    }

    private void createJar(Manifest manifest) {

        int BUFFER_SIZE = 10240;
        ArrayList<File> files = new ArrayList();
        this.obtainFilesToJared(files, "tempDirectory"+plugin.getArtifactId());

        try {
            byte buffer[] = new byte[BUFFER_SIZE];
            // Open archive file
            FileOutputStream stream = new FileOutputStream(plugin.obtainFileName());
            JarOutputStream out = new JarOutputStream(stream, manifest);

            int length = files.size();
            for (int i = 0; i < length; i++) {
                // Add archive entry
                JarEntry jarAdd = new JarEntry(files.get(i).getName());
                jarAdd.setTime(files.get(i).lastModified());
                out.putNextEntry(jarAdd);

                // Write file to archive
                FileInputStream in = new FileInputStream(files.get(i));
                while (true) {
                    int nRead = in.read(buffer, 0, buffer.length);
                    if (nRead <= 0) {
                        break;
                    }
                    out.write(buffer, 0, nRead);
                }
                in.close();
            }

            out.close();
            stream.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    private void obtainFilesToJared( ArrayList<File> files, String directory ){

        File file = new File(directory);

        File[] arrayFiles = file.listFiles();

        for( int i = 0; i < arrayFiles.length; i++ ){

            if( arrayFiles[i].isDirectory() ){
                this.obtainFilesToJared(files, arrayFiles[i].getAbsolutePath());
            }else{
                files.add(arrayFiles[i]);
            }

        }
        
    }

    private Manifest updateManifestContent(Manifest manifest, ArrayList<Plugin> toUpdate) {

        Attributes att = manifest.getMainAttributes();
        String values = att.getValue("Class-Path");

        String dependencies[] = values.split(" ");
        StringBuffer resolveDep = new StringBuffer();

        int size = toUpdate.size();
        for (int i = 0; i < size; i++) {
            Plugin plugin = toUpdate.get(i);

            for (int j = 0; j < dependencies.length; j++) {
                if (dependencies[j].startsWith(plugin.getArtifactId())) {
                    dependencies[j] = plugin.obtainFileName();
                    break;
                }
            }
        }

        for( int i=0; i<dependencies.length; i++ ){
            resolveDep.append(dependencies[i] + " ");
        }

        att.put(new Attributes.Name("Class-Path"), new String(resolveDep));

        return manifest;
    }

    private void copyJar( boolean temp ) {

        File file = new File("tempDirectory" + plugin.getArtifactId());
        if (!file.isDirectory()) {
            file.mkdir();
        }

        try {

            File inputFile;
            File outputFile;

            if( temp ){
                inputFile = new File(plugin.obtainFileName());
                outputFile = new File(file.getAbsolutePath() + plugin.obtainFileName());
            }else{
                inputFile = new File(file.getAbsolutePath() + plugin.obtainFileName());
                outputFile = new File(plugin.obtainFileName());
            }

            FileReader in = new FileReader(inputFile);
            FileWriter out = new FileWriter(outputFile);
            int c;

            while ((c = in.read()) != -1) {
                out.write(c);
            }

            in.close();
            out.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private Manifest unJar() {
        Enumeration entries;
        JarFile jarFile;
        Manifest manifest = null;

        try {
            jarFile = new JarFile(plugin.obtainFileName());
            manifest = jarFile.getManifest();

            entries = jarFile.entries();

            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();

                if (entry.isDirectory()) {
                    (new File(entry.getName())).mkdir();
                    continue;
                }

                //WRITE FILE
                InputStream in = jarFile.getInputStream(entry);
                OutputStream out = new BufferedOutputStream(new FileOutputStream(entry.getName()));
                byte[] buffer = new byte[1024];
                int len;

                while ((len = in.read(buffer)) >= 0) {
                    out.write(buffer, 0, len);
                }

                in.close();
                out.close();
            }

            jarFile.close();
        } catch (IOException ioex) {
            ioex.printStackTrace();
        }

        return manifest;
    }
}
